/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Manish Mehta 
 												
Class							:				PatternTracker
 
Function						: 				A class for extracting patterns from the execution trace
****************************************************************************/
package behavioradaptation.reasoner;


import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import planninglanguage.plans.BasicOperatorPlan;
import trace.Entry;
import trace.Testtrace;
import trace.Trace;
import base.WargusGameState;
import base.WargusPlayer;
import base.WargusStateImporter;
import base.WargusUnit;
import behavioradaptation.analysisdata.AbstractedTrace;
import behavioradaptation.analysisdata.AttackData;
import behavioradaptation.analysisdata.IdleData;
import behavioradaptation.analysisdata.KillData;
import behavioradaptation.analysisdata.ResourceData;

public class PatternTracker {
/*	String []attackunits = {"peon", "footman", "grunt","skeleton",
	 "archer","axethrower","ballista","catapult","dwarven-demolition-squad",
	 "goblin-sappers","knight","ogre","paladin","mage","flying-machine",
	 "zeppelin","gryphon","dragon","human-oil-tanker","orc-oil-tanker",
	 "destroyer","transport","submarine","battle-ship"};
	String []bldgs = {"town-hall","keep","castle","great-hall","stronghold","fortress", 
			"human-barracks","orc-barracks", "human-shipyard", "orc-shipyard", "gryphon-aviary", 
			"mage-tower","gnomish-inventor", "goblin-alchemist", "human-scout-tower","orc-scout-tower",
			"human-guard-tower","orc-guard-tower","human-cannon-tower","orc-cannon-tower",
			"elven-lumber-mill", "troll-lumber-mill", "human-blacksmith", "orc-blacksmith", 
			"stables", "ogre-mound", "church", "dragon-roost", "temple-of-the-damned", "altar-of-storms", 
			"farm","pig-farm","human-oil-rig", "orc-oil-rig", "human-foundry", "orc-foundry", 
			"human-oil-refinery", "orc-oil-refinery"};*/
	String []m_selfUnits = {"peasant", "footman", "archer","axethrower","ballista","catapult", 
			         "town-hall","keep","castle","great-hall","stronghold","fortress", 
					 "human-barracks"};
	String []m_enemyUnits = {"grunt", "axethrower"};
	int ATTACK_THRESHOLD_DISTANCE = 35;
	int PEASANT_IDLE_THRESHOLD_CYCLES = 250;
	public Trace m_trace;
	public class IdleMarkers{
		public WargusUnit m_unit;
		public int m_curplanId;
		public int m_traceIndex;
		public double m_startCycle;
	}
	public PatternTracker(Trace a_trace)
	{
		m_trace = a_trace;
		
	}
	/*
	 * Abstract the data from the trace and fill in the abstracted trace 
	 */
	public void abstractDataFromTrace(AbstractedTrace a_abstractedTrace) throws Exception
	{
		Vector<Entry> entlist = m_trace.getTrace();
		Vector<List> selfunitlist = a_abstractedTrace.getSelfUnits();
		Vector<List> enemyunitlist = a_abstractedTrace.getEnemyUnits();
//		Vector<ResourceData> enemyresourcelist = a_abstractedTrace.getEnemyResourceData();
		Vector<ResourceData> selfresourcelist = a_abstractedTrace.getSelfResourceData();
		String resource = null;
		int gold = 0;
		int wood = 0; 
		int oil = 0;
		int food = 0;
		int demand = 0;
		for(int i = 0; i < entlist.size(); i++)
		{
			WargusGameState gamestate = entlist.get(i).getGameState();			
			for(WargusPlayer p:gamestate.getPlayers()) 
			{				
					int playerID = p.getID();
					List<WargusUnit> unitl = new LinkedList<WargusUnit>();
					unitl.addAll(p.getUnits());
				
					//System.out.println("Hello");
					if(playerID== 0)
					{	
						gold = p.getGold();
						wood = p.getWood();
						oil = p.getOil();
						food = p.getFood();
						demand = p.getDemand();
						
						ResourceData rdata = new ResourceData();
						rdata.gold = gold;
						rdata.wood = wood;
						rdata.Oil = oil;
						rdata.food = food;
						rdata.demand = demand;
						selfunitlist.add(unitl);
						selfresourcelist.add(rdata);
					}
					else if(playerID == 1) 
						enemyunitlist.add(unitl);
			}			
		}
		// find the idle intervals 
		findIdleIntervals(0, a_abstractedTrace);
		// find time intervals for attack 
		getTimeintervalsOfAttack(a_abstractedTrace);
		// find kill data 
		findKillData(a_abstractedTrace);
								
	}
	
	/*
	 * Find Idle Intervals for given units under player control 
	 */


	public void findIdleIntervals(int a_playerID, AbstractedTrace a_abstractedTrace)
	{
		HashMap unitidlemap = new HashMap();
		Vector<IdleData> idledata = a_abstractedTrace.getIdleData();
		Vector <List> m_selfUnitList = a_abstractedTrace.getSelfUnits();
		for(int i = 0; i <m_selfUnitList.size(); i++)
		{
			List s_unitlist = m_selfUnitList.get(i);
			Entry ent = (Entry)m_trace.getTrace().get(i);	
			for(int k = 0; k <s_unitlist.size(); k++)			
			{
				WargusUnit s_unit = (WargusUnit)s_unitlist.get(k);			
				String sunit = String.valueOf(s_unit.getUnitID());
				// Store the IDLE units when it first becomes idle 
				// into a temporary list
				if(s_unit.getStatus().equals("idle"))
				{	
					IdleMarkers id = (IdleMarkers)unitidlemap.get(sunit);
					if(id == null)
					{							
						IdleMarkers id_mark = new IdleMarkers();
						id_mark.m_traceIndex = i;
						id_mark.m_startCycle = ent.getTime();
						id_mark.m_unit = s_unit;
						if((ent.getPlan() instanceof BasicOperatorPlan) &&	
						(ent.getEventType() == Entry.EventType.start) &&
						(((BasicOperatorPlan)ent.getPlan()).getUnitID() == s_unit.getUnitID()))
							System.out.println("Current Status is Idle. However taking part in BO plan");
						else
							unitidlemap.put(sunit, id_mark);	
					}		
				}	
				// This units has become active again and check 
				// the idle interval 
				else 
				{
					IdleMarkers id = (IdleMarkers)unitidlemap.get(sunit);
					// If the unit is there in the temporary list 
					if(id != null)
					{
						// and the idle interval is sufficient one
						if((ent.getTime()- id.m_startCycle) > PEASANT_IDLE_THRESHOLD_CYCLES)
						{
							// add it to the idle list 
							IdleData idata = new IdleData(id.m_traceIndex, i, id.m_startCycle, ent.getTime(), s_unit);
							idledata.add(idata);
						}	
						// and remove it from the temporary list 
						unitidlemap.remove(sunit);				
					}	
				}	
			 }		
		}
		// Add rest of the units present in the temporary 
		// list by checking if the idle interval was high enough 
		Set idlekeyset = unitidlemap.keySet();
		Iterator idle = idlekeyset.iterator(); 
		Entry ent = m_trace.get(m_trace.size()-1);
		while(idle.hasNext())
		{	
			String sunit = (String)idle.next();
			IdleMarkers id = (IdleMarkers)unitidlemap.get(sunit);
			if(id != null)
			{
				if((ent.getTime()- id.m_startCycle) > PEASANT_IDLE_THRESHOLD_CYCLES)
				{
					IdleData idata = new IdleData(id.m_traceIndex, (m_trace.size()-1), id.m_startCycle, ent.getTime(), id.m_unit);
					idledata.add(idata);
				}
			}		
		}	
	}
	/*
	 * Extract Kill Data from the execution trace
	 */
	public void findKillData(AbstractedTrace a_abstractedTrace) throws Exception
	{
		Vector<List> unitList = a_abstractedTrace.getSelfUnits();
		Vector<KillData> kdatalist = a_abstractedTrace.getKillData();
		Vector <List> a_selfUnitList = a_abstractedTrace.getSelfUnits();
		HashMap tmplist = new HashMap();
		for(int i = 0; i <unitList.size(); i++)
		{
			List s_unitlist = unitList.get(i);
			for(int j = 0; j < s_unitlist.size();j++)
			{	
				WargusUnit s_unit = (WargusUnit)s_unitlist.get(j);
				String sunit = WargusStateImporter.unitTypeToString(s_unit.getType());				
				if(sunit.contains("destroyed") || (s_unit.getHitPoints() == 0))
				{
					if(tmplist.get(String.valueOf(s_unit.getUnitID())) == null)
					{	
						WargusUnit u = getUnitKilled(s_unit.getUnitID(), a_selfUnitList.get(i-1));
						Entry e = (Entry)m_trace.get(i);
						int killcycle = (int)e.getTime();
						int attackindex = getAttackStartCycle(j,i, a_selfUnitList);	
						int attackstartcycle = 0;
						if(attackindex >=0 )
						{
							attackstartcycle = (int)((Entry)m_trace.get(attackindex)).getTime();
							KillData kdata = new KillData(i-1,u, killcycle, attackstartcycle);
							kdatalist.add(kdata);
						}	
						tmplist.put(String.valueOf(u.getUnitID()), u);
						
					}	
				}
			}
		}	
		
	}
	/*
	 * Get the Cycle at which the attack started 
	 */
	public int getAttackStartCycle(int unitindex, int traceindex, Vector<List> selflist)
	{
		int hitpoints = 0;
		int uindex = 0;
		boolean decreasestarted = false;
		
		for(int k = traceindex; k >= 0; k--)
		{
			List s_unitlist = selflist.get(k);		
			if(unitindex >= s_unitlist.size())
				break;
			WargusUnit s_unit = (WargusUnit)s_unitlist.get(unitindex);
			Integer currenthitpoint = s_unit.getHitPoints();
			int currenthitpoints = s_unit.getHitPoints();
			// If the currenhit points have started decreasing 
			if(currenthitpoints > hitpoints)
			{	
				hitpoints = s_unit.getHitPoints();
				uindex = k;
				decreasestarted = true;
			}	
		}	
		if(decreasestarted)
			return uindex;
		else
			return -1;
	}
	/*
	 * Get The unit killed 
	 */
	public WargusUnit getUnitKilled(int unitid, List unitlist)
	{
		for(int j = 0; j < unitlist.size();j++)
		{
			WargusUnit s_unit = (WargusUnit)unitlist.get(j);
			if(s_unit.getUnitID() == unitid)
				return s_unit;
		}
		return null;
	}
	
	/*
	 * Get the Start and End Cycle at which the attack started 
	 */
	public void getTimeintervalsOfAttack(AbstractedTrace a_abstractedTrace) throws Exception
	{
		Vector<AttackData> attdatalist = a_abstractedTrace.getAttackData();
		boolean attack_happened = false;
		AttackData  attack_data = null;
		Vector <List> a_selfUnitList = a_abstractedTrace.getSelfUnits();
		Vector <List> a_enemyUnitList = a_abstractedTrace.getEnemyUnits();
		for(int i = 0; i <a_selfUnitList.size(); i++)
		{
			attack_happened = false;
			List s_unitlist = a_selfUnitList.get(i);
			List e_unitlist = a_enemyUnitList.get(i);
			
			for(int k = 0; k <s_unitlist.size(); k++)			
			{
				WargusUnit s_unit = (WargusUnit)s_unitlist.get(k);			
				for(int j = 0; j < m_selfUnits.length;j++)
				{	
					String sunit_type = WargusStateImporter.unitTypeToString(s_unit.getType());
					if(sunit_type.equals(m_selfUnits[j]))
					{
						for(int l=0; l <e_unitlist.size(); l++)
						{
							WargusUnit e_unit = (WargusUnit)e_unitlist.get(l);
							for(int m = 0; m < m_enemyUnits.length;m++)
							{	
								String eunit_type = WargusStateImporter.unitTypeToString(e_unit.getType());
								if(eunit_type.equals(m_enemyUnits[m]))
								{
									int s_X = s_unit.getLocX();
									int s_Y = s_unit.getLocY();
									int e_X = e_unit.getLocX();
									int e_Y = e_unit.getLocY();				
									int x_dist = (s_X - e_X)*(s_X - e_X);
									int y_dist = (s_Y - e_Y)*(s_Y - e_Y);
									// Threshold distance for measuring whether the attack started or not 
									if(Math.sqrt(x_dist + y_dist) < ATTACK_THRESHOLD_DISTANCE)
									{
										if(!attack_happened)
										{	
											attack_happened = true;
											attack_data = new AttackData();
											attack_data.setTraceIndex(i);		
											attack_data.addEnemyUnit(e_unit);
											attack_data.addSelfUnit(s_unit);
										}
										else
										{		
											// add the enemy units and self units 
											// to the attack data 
											attack_data.addEnemyUnit(e_unit);
											attack_data.addSelfUnit(s_unit);
										}	
									}
									
								}	
							}		
						}					
					}	
				}
			}	
			// if attack happened add it to the attack list 
			if(attack_happened)
				attdatalist.add(attack_data);
		}
	}
	public static void main(String[] args) throws Exception {
		Testtrace t = new Testtrace();
		t.readSerialize("game-trace-lite-4-inf-cases.dat");
		PatternTracker p = new PatternTracker(t.tr);
		AbstractedTrace abstractedtrace = new AbstractedTrace(t.tr);
		p.abstractDataFromTrace(abstractedtrace);
		Vector<IdleData> Idledata = abstractedtrace.getIdleData();
		Vector<AttackData> adatalist = abstractedtrace.getAttackData();
		Vector<KillData> killdata = abstractedtrace.getKillData();
		try {
			   FileWriter outFile = new FileWriter("pattermatching-4-lite.txt");
			   PrintWriter out = new PrintWriter(outFile);
			    for(int k = 0; k < Idledata.size(); k++)
			    {
			    	IdleData s_idledata = (IdleData)Idledata.get(k);
			    	WargusUnit s_unit = s_idledata.getUnit();
					System.out.print(" " + s_unit.getType());
					out.print(" " + s_unit.getType());		
					System.out.print(" with Id: " + s_unit.getUnitID());
					out.print(" with Id: " + s_unit.getUnitID());
					out.print(" " + s_unit.getType());		
					System.out.print(" with Id: " + s_unit.getUnitID());
					out.print(" Starting Cycle: " + s_idledata.getStartCycle());
					out.print(" Ending Cycle: " + s_idledata.getEndCycle());
					out.println();
			
			    }	
				for(int i=0; i <adatalist.size(); i++)
				{
					AttackData un = (AttackData)adatalist.get(i);
					Vector <WargusUnit> sunit = un.getSelfUnits();
					System.out.println("Self Units being attacked");
					out.println("Self Units being attacked");
					
					for (int j = 0; j < sunit.size(); j++)
					{
						WargusUnit s_unit = (WargusUnit)sunit.get(j);
						System.out.print(" " + s_unit.getType());
						out.print(" " + s_unit.getType());		
						System.out.print(" with Id: " + s_unit.getUnitID());
						out.print(" with Id: " + s_unit.getUnitID());
					}	
					System.out.println();		
					out.println();
					System.out.println("Enemy Units Attacking");
					out.println("Enemy Units Attacking");
					
					Vector <WargusUnit> eunit = un.getEnemyUnits();
					for (int j = 0; j < eunit.size(); j++)
					{
						WargusUnit e_unit = (WargusUnit)eunit.get(j);
						System.out.print(" " + e_unit.getType());
						out.print(" " + e_unit.getType());												
						System.out.print(" with Id: " + e_unit.getUnitID());
						out.print(" with Id: " + e_unit.getUnitID());
			
					}	
					
					System.out.println();
					out.println();
					
				}
				out.flush();
				out.close();
		}catch (IOException e){
		  e.printStackTrace();}   	
	}	
}
